using Microsoft.Extensions.Logging;
using Sage.Http.Models.Internal;
using Sage.Http.Options;
using System.Diagnostics;

namespace Sage.Http.Handlers
{
    /// <summary>
    /// 日志处理器，用于记录HTTP请求和响应的详细信息
    /// </summary>
    /// <remarks>
    /// 初始化日志处理器
    /// </remarks>
    /// <param name="logger">日志记录器</param>
    /// <param name="options">日志选项</param>
    public class LoggingHandler(ILogger<LoggingHandler>? logger = null, LoggingOptions? options = null) : DelegatingHandler
    {
        private readonly ILogger<LoggingHandler>? _logger = logger;
        private readonly LoggingOptions _options = options ?? new LoggingOptions();

        /// <summary>
        /// 发送HTTP请求并记录日志
        /// </summary>
        /// <param name="request">HTTP请求</param>
        /// <param name="cancellationToken">取消令牌</param>
        /// <returns>HTTP响应</returns>
        protected override async Task<HttpResponseMessage> SendAsync(HttpRequestMessage request, CancellationToken cancellationToken)
        {
            var stopwatch = Stopwatch.StartNew();
            var requestId = _options.LogRequestId ? Guid.NewGuid().ToString("N")[..8] : string.Empty;

            // 记录请求日志
            if (_options.LogRequests)
            {
                await LogRequestAsync(request, requestId);
            }

            try
            {
                var response = await base.SendAsync(request, cancellationToken);
                stopwatch.Stop();

                // 记录响应日志
                if (_options.LogResponses)
                {
                    await LogResponseAsync(response, requestId, stopwatch.ElapsedMilliseconds);
                }

                return response;
            }
            catch (Exception ex)
            {
                stopwatch.Stop();
                if (_options.LogTiming && _options.LogRequestId)
                {
                    _logger?.LogError(ex, "请求失败 [RequestId: {RequestId}] 耗时: {ElapsedMs}ms", requestId, stopwatch.ElapsedMilliseconds);
                }
                else if (_options.LogRequestId)
                {
                    _logger?.LogError(ex, "请求失败 [RequestId: {RequestId}]", requestId);
                }
                else if (_options.LogTiming)
                {
                    _logger?.LogError(ex, "请求失败 耗时: {ElapsedMs}ms", stopwatch.ElapsedMilliseconds);
                }
                else
                {
                    _logger?.LogError(ex, "请求失败");
                }
                throw;
            }
        }

        /// <summary>
        /// 记录请求日志
        /// </summary>
        /// <param name="request">HTTP请求</param>
        /// <param name="requestId">请求ID</param>
        private async Task LogRequestAsync(HttpRequestMessage request, string requestId)
        {
            var logData = new RequestLogData
            {
                RequestId = _options.LogRequestId ? requestId : null,
                Method = request.Method.ToString(),
                Uri = request.RequestUri?.ToString() ?? string.Empty,
                Headers = _options.LogRequestHeaders ? GetRequestHeadersString(request) : null,
                Body = _options.LogRequestBody ? await ReadContentSafelyAsync(request.Content) : null
            };

            LogRequest(logData);
        }

        /// <summary>
        /// 记录响应日志
        /// </summary>
        /// <param name="response">HTTP响应</param>
        /// <param name="requestId">请求ID</param>
        /// <param name="elapsedMs">耗时（毫秒）</param>
        private async Task LogResponseAsync(HttpResponseMessage response, string requestId, long elapsedMs)
        {
            var logData = new ResponseLogData
            {
                RequestId = _options.LogRequestId ? requestId : null,
                StatusCode = (int)response.StatusCode,
                ElapsedMs = _options.LogTiming ? elapsedMs : null,
                Headers = _options.LogResponseHeaders ? GetResponseHeadersString(response) : null,
                Body = _options.LogResponseBody ? await ReadContentSafelyAsync(response.Content) : null
            };

            LogResponse(logData);
        }

        /// <summary>
        /// 记录请求日志的具体实现
        /// </summary>
        /// <param name="data">请求日志数据</param>
        private void LogRequest(RequestLogData data)
        {
            // 根据不同的配置组合使用不同的固定消息模板
            switch ((data.RequestId != null, data.Headers != null, data.Body != null))
            {
                case (true, true, true):
                    _logger?.LogInformation(
                        "发送请求 [RequestId: {RequestId}] {Method} {Uri}\nHeaders: {Headers}\nBody: {Body}",
                        data.RequestId, data.Method, data.Uri, data.Headers, data.Body);
                    break;
                case (true, true, false):
                    _logger?.LogInformation(
                        "发送请求 [RequestId: {RequestId}] {Method} {Uri}\nHeaders: {Headers}",
                        data.RequestId, data.Method, data.Uri, data.Headers);
                    break;
                case (true, false, true):
                    _logger?.LogInformation(
                        "发送请求 [RequestId: {RequestId}] {Method} {Uri}\nBody: {Body}",
                        data.RequestId, data.Method, data.Uri, data.Body);
                    break;
                case (true, false, false):
                    _logger?.LogInformation(
                        "发送请求 [RequestId: {RequestId}] {Method} {Uri}",
                        data.RequestId, data.Method, data.Uri);
                    break;
                case (false, true, true):
                    _logger?.LogInformation(
                        "发送请求 {Method} {Uri}\nHeaders: {Headers}\nBody: {Body}",
                        data.Method, data.Uri, data.Headers, data.Body);
                    break;
                case (false, true, false):
                    _logger?.LogInformation(
                        "发送请求 {Method} {Uri}\nHeaders: {Headers}",
                        data.Method, data.Uri, data.Headers);
                    break;
                case (false, false, true):
                    _logger?.LogInformation(
                        "发送请求 {Method} {Uri}\nBody: {Body}",
                        data.Method, data.Uri, data.Body);
                    break;
                case (false, false, false):
                    _logger?.LogInformation(
                        "发送请求 {Method} {Uri}",
                        data.Method, data.Uri);
                    break;
            }
        }

        /// <summary>
        /// 记录响应日志的具体实现
        /// </summary>
        /// <param name="data">响应日志数据</param>
        private void LogResponse(ResponseLogData data)
        {
            // 根据不同的配置组合使用不同的固定消息模板
            switch ((data.RequestId != null, data.ElapsedMs.HasValue, data.Headers != null, data.Body != null))
            {
                case (true, true, true, true):
                    _logger?.LogInformation(
                        "收到响应 [RequestId: {RequestId}] {StatusCode} 耗时: {ElapsedMs}ms\nHeaders: {Headers}\nBody: {Body}",
                        data.RequestId, data.StatusCode, data.ElapsedMs!.Value, data.Headers, data.Body);
                    break;
                case (true, true, true, false):
                    _logger?.LogInformation(
                        "收到响应 [RequestId: {RequestId}] {StatusCode} 耗时: {ElapsedMs}ms\nHeaders: {Headers}",
                        data.RequestId, data.StatusCode, data.ElapsedMs!.Value, data.Headers);
                    break;
                case (true, true, false, true):
                    _logger?.LogInformation(
                        "收到响应 [RequestId: {RequestId}] {StatusCode} 耗时: {ElapsedMs}ms\nBody: {Body}",
                        data.RequestId, data.StatusCode, data.ElapsedMs!.Value, data.Body);
                    break;
                case (true, true, false, false):
                    _logger?.LogInformation(
                        "收到响应 [RequestId: {RequestId}] {StatusCode} 耗时: {ElapsedMs}ms",
                        data.RequestId, data.StatusCode, data.ElapsedMs!.Value);
                    break;
                case (true, false, true, true):
                    _logger?.LogInformation(
                        "收到响应 [RequestId: {RequestId}] {StatusCode}\nHeaders: {Headers}\nBody: {Body}",
                        data.RequestId, data.StatusCode, data.Headers, data.Body);
                    break;
                case (true, false, true, false):
                    _logger?.LogInformation(
                        "收到响应 [RequestId: {RequestId}] {StatusCode}\nHeaders: {Headers}",
                        data.RequestId, data.StatusCode, data.Headers);
                    break;
                case (true, false, false, true):
                    _logger?.LogInformation(
                        "收到响应 [RequestId: {RequestId}] {StatusCode}\nBody: {Body}",
                        data.RequestId, data.StatusCode, data.Body);
                    break;
                case (true, false, false, false):
                    _logger?.LogInformation(
                        "收到响应 [RequestId: {RequestId}] {StatusCode}",
                        data.RequestId, data.StatusCode);
                    break;
                case (false, true, true, true):
                    _logger?.LogInformation(
                        "收到响应 {StatusCode} 耗时: {ElapsedMs}ms\nHeaders: {Headers}\nBody: {Body}",
                        data.StatusCode, data.ElapsedMs!.Value, data.Headers, data.Body);
                    break;
                case (false, true, true, false):
                    _logger?.LogInformation(
                        "收到响应 {StatusCode} 耗时: {ElapsedMs}ms\nHeaders: {Headers}",
                        data.StatusCode, data.ElapsedMs!.Value, data.Headers);
                    break;
                case (false, true, false, true):
                    _logger?.LogInformation(
                        "收到响应 {StatusCode} 耗时: {ElapsedMs}ms\nBody: {Body}",
                        data.StatusCode, data.ElapsedMs!.Value, data.Body);
                    break;
                case (false, true, false, false):
                    _logger?.LogInformation(
                        "收到响应 {StatusCode} 耗时: {ElapsedMs}ms",
                        data.StatusCode, data.ElapsedMs!.Value);
                    break;
                case (false, false, true, true):
                    _logger?.LogInformation(
                        "收到响应 {StatusCode}\nHeaders: {Headers}\nBody: {Body}",
                        data.StatusCode, data.Headers, data.Body);
                    break;
                case (false, false, true, false):
                    _logger?.LogInformation(
                        "收到响应 {StatusCode}\nHeaders: {Headers}",
                        data.StatusCode, data.Headers);
                    break;
                case (false, false, false, true):
                    _logger?.LogInformation(
                        "收到响应 {StatusCode}\nBody: {Body}",
                        data.StatusCode, data.Body);
                    break;
                case (false, false, false, false):
                    _logger?.LogInformation(
                        "收到响应 {StatusCode}",
                        data.StatusCode);
                    break;
            }
        }

        /// <summary>
        /// 获取请求头部信息字符串
        /// </summary>
        /// <param name="request">HTTP请求</param>
        /// <returns>头部信息字符串</returns>
        private string GetRequestHeadersString(HttpRequestMessage request)
        {
            var headerList = new List<string>();

            // 添加请求头
            headerList.AddRange(request.Headers
                .Where(h => !IsSensitiveHeader(h.Key))
                .Select(h => $"{h.Key}: {string.Join(", ", h.Value)}"));

            // 添加内容头
            if (request.Content?.Headers != null)
            {
                headerList.AddRange(request.Content.Headers
                    .Where(h => !IsSensitiveHeader(h.Key))
                    .Select(h => $"{h.Key}: {string.Join(", ", h.Value)}"));
            }

            return string.Join(", ", headerList);
        }

        /// <summary>
        /// 获取响应头部信息字符串
        /// </summary>
        /// <param name="response">HTTP响应</param>
        /// <returns>头部信息字符串</returns>
        private string GetResponseHeadersString(HttpResponseMessage response)
        {
            var headerList = new List<string>();

            // 添加响应头
            headerList.AddRange(response.Headers
                .Where(h => !IsSensitiveHeader(h.Key))
                .Select(h => $"{h.Key}: {string.Join(", ", h.Value)}"));

            // 添加内容头
            if (response.Content?.Headers != null)
            {
                headerList.AddRange(response.Content.Headers
                    .Where(h => !IsSensitiveHeader(h.Key))
                    .Select(h => $"{h.Key}: {string.Join(", ", h.Value)}"));
            }

            return string.Join(", ", headerList);
        }

        /// <summary>
        /// 安全地读取HTTP内容，不消耗原始流
        /// </summary>
        /// <param name="content">HTTP内容</param>
        /// <returns>内容字符串</returns>
        private async Task<string> ReadContentSafelyAsync(HttpContent? content)
        {
            if (content == null) return string.Empty;

            try
            {
                // 检查内容长度
                if (content.Headers.ContentLength == 0)
                    return string.Empty;

                // 先检查MIME类型，避免不必要的内容读取
                if (IsBinaryByMimeType(content, _options))
                {
                    var contentLength = content.Headers.ContentLength ?? 0;
                    var contentType = content.Headers.ContentType?.MediaType ?? "unknown";
                    return $"[Binary content: {contentType}, Size: {contentLength} bytes]";
                }

                // 一次性读取为字节数组（最安全的方式）
                byte[] contentBytes;
                try
                {
                    contentBytes = await content.ReadAsByteArrayAsync();
                }
                catch (Exception ex)
                {
                    _logger?.LogWarning(ex, "无法读取HTTP内容为字节数组");
                    var contentLength = content.Headers.ContentLength ?? 0;
                    var contentType = content.Headers.ContentType?.MediaType ?? "unknown";
                    return $"[Unable to read content: {contentType}, Size: {contentLength} bytes]";
                }

                if (contentBytes.Length == 0)
                    return string.Empty;

                // 基于字节数组进行二进制检测
                if (_options.BinaryDetection.EnableContentAnalysis &&
                    IsBinaryByContentAnalysis(contentBytes, _options))
                {
                    var contentType = content.Headers.ContentType?.MediaType ?? "unknown";
                    return $"[Binary content: {contentType}, Size: {contentBytes.Length} bytes]";
                }

                // 安全地转换为字符串
                string contentString;
                try
                {
                    contentString = System.Text.Encoding.UTF8.GetString(contentBytes);
                }
                catch (Exception ex)
                {
                    // UTF-8解码失败，很可能是二进制内容
                    _logger?.LogDebug(ex, "UTF-8解码失败，内容可能是二进制");
                    var contentType = content.Headers.ContentType?.MediaType ?? "unknown";
                    return $"[Binary content (decode failed): {contentType}, Size: {contentBytes.Length} bytes]";
                }

                // 如果内容过长，进行截断
                if (contentString.Length > _options.MaxContentLength)
                {
                    return TruncateAtCharacterBoundary(contentString, _options.MaxContentLength) + "... (truncated)";
                }

                return contentString;
            }
            catch (Exception ex)
            {
                _logger?.LogWarning(ex, "无法读取HTTP内容");
                return "[无法读取内容]";
            }
        }

        /// <summary>
        /// 检查是否为敏感头部信息
        /// </summary>
        /// <param name="headerName">头部名称</param>
        /// <returns>是否为敏感头部</returns>
        private bool IsSensitiveHeader(string headerName)
        {
            return _options.SensitiveHeaders.Contains(headerName);
        }

        /// <summary>
        /// 基于MIME类型检查是否为二进制内容
        /// </summary>
        /// <param name="content">HTTP内容</param>
        /// <param name="options">日志选项配置</param>
        /// <returns>是否为二进制内容</returns>
        private static bool IsBinaryByMimeType(HttpContent content, LoggingOptions options)
        {
            var mediaType = content.Headers.ContentType?.MediaType?.ToLowerInvariant();

            if (string.IsNullOrEmpty(mediaType))
                return false;

            // 检查自定义文本类型（优先级最高）
            if (options.BinaryDetection.CustomTextTypes.Any(type =>
                mediaType.StartsWith(type.ToLowerInvariant(), StringComparison.OrdinalIgnoreCase)))
                return false;

            // 检查自定义二进制类型
            if (options.BinaryDetection.CustomBinaryTypes.Any(type =>
                mediaType.StartsWith(type.ToLowerInvariant(), StringComparison.OrdinalIgnoreCase)))
                return true;

            // 明确的文本类型
            var textTypes = new[]
            {
                "text/", "application/json", "application/xml", "application/javascript",
                "application/x-www-form-urlencoded", "application/soap+xml",
                "application/xhtml+xml", "application/rss+xml", "application/atom+xml"
            };

            if (textTypes.Any(type => mediaType.StartsWith(type, StringComparison.OrdinalIgnoreCase)))
                return false;

            // 明确的二进制类型
            var binaryTypes = new[]
            {
                "image/", "video/", "audio/", "application/octet-stream",
                "application/pdf", "application/zip", "application/x-rar-compressed",
                "application/x-7z-compressed", "application/x-tar", "application/gzip",
                "application/x-gzip", "application/x-bzip2", "application/x-compress",
                "application/vnd.ms-excel", "application/vnd.openxmlformats-officedocument",
                "application/msword", "application/vnd.ms-powerpoint", "application/vnd.ms-",
                "application/vnd.oasis.opendocument", "application/vnd.adobe.flash",
                "font/", "application/font-", "application/x-font-", "application/vnd.ms-fontobject",
                "application/x-shockwave-flash", "application/x-executable", "application/x-dosexec",
                "application/java-archive", "application/x-java-archive", "application/x-debian-package",
                "application/x-rpm", "application/x-apple-diskimage", "application/vnd.android.package-archive"
            };

            return binaryTypes.Any(type => mediaType.StartsWith(type, StringComparison.OrdinalIgnoreCase));
        }

        /// <summary>
        /// 基于字节数组进行二进制内容分析
        /// </summary>
        /// <param name="contentBytes">内容字节数组</param>
        /// <param name="options">日志选项配置</param>
        /// <returns>是否为二进制内容</returns>
        private static bool IsBinaryByContentAnalysis(byte[] contentBytes, LoggingOptions options)
        {
            try
            {
                var sampleSize = Math.Min(contentBytes.Length, options.BinaryDetection.ContentAnalysisSampleSize);
                return ContainsBinaryData(contentBytes, sampleSize, options);
            }
            catch
            {
                return false;
            }
        }

        /// <summary>
        /// 检查字节数组是否包含二进制数据特征
        /// </summary>
        /// <param name="buffer">字节缓冲区</param>
        /// <param name="length">要检查的长度</param>
        /// <param name="options">日志选项配置</param>
        /// <returns>是否包含二进制数据</returns>
        private static bool ContainsBinaryData(byte[] buffer, int length, LoggingOptions options)
        {
            int nullBytes = 0;
            int controlChars = 0;
            int highBitChars = 0;

            for (int i = 0; i < length; i++)
            {
                byte b = buffer[i];

                // 检查 null 字节
                if (b == 0)
                {
                    nullBytes++;
                    continue;
                }

                // 检查控制字符（除了常见的文本控制字符）
                if (b < 32 && b != 9 && b != 10 && b != 13) // 排除 Tab, LF, CR
                {
                    controlChars++;
                }

                // 检查高位字符
                if (b > 127)
                {
                    highBitChars++;
                }
            }

            // 启发式规则：
            // 1. 如果有 null 字节，很可能是二进制
            if (nullBytes > 0)
                return true;

            // 2. 如果控制字符比例过高，可能是二进制
            double controlRatio = (double)controlChars / length;
            if (controlRatio > options.BinaryDetection.ControlCharThreshold)
                return true;

            // 3. 检查是否符合 UTF-8 编码模式
            if (!IsValidUtf8(buffer, length))
            {
                // 如果不是有效的 UTF-8 且高位字符很多，可能是二进制
                double highBitRatio = (double)highBitChars / length;
                if (highBitRatio > options.BinaryDetection.HighBitCharThreshold)
                    return true;
            }

            return false;
        }

        /// <summary>
        /// 检查字节数组是否为有效的UTF-8编码
        /// </summary>
        /// <param name="buffer">字节缓冲区</param>
        /// <param name="length">要检查的长度</param>
        /// <returns>是否为有效的UTF-8</returns>
        private static bool IsValidUtf8(byte[] buffer, int length)
        {
            try
            {
                var text = System.Text.Encoding.UTF8.GetString(buffer, 0, length);
                var reencoded = System.Text.Encoding.UTF8.GetBytes(text);

                // 简单检查：如果重新编码后长度差异很大，可能不是有效的 UTF-8
                return Math.Abs(reencoded.Length - length) <= length * 0.1;
            }
            catch
            {
                return false;
            }
        }

        /// <summary>
        /// 在字符边界处截断字符串
        /// </summary>
        /// <param name="text">原始文本</param>
        /// <param name="maxLength">最大长度</param>
        /// <returns>截断后的文本</returns>
        private static string TruncateAtCharacterBoundary(string text, int maxLength)
        {
            if (text.Length <= maxLength) return text;

            // 在字符边界处截断，避免截断多字节字符
            var span = text.AsSpan(0, maxLength);
            for (int i = span.Length - 1; i >= 0; i--)
            {
                if (!char.IsHighSurrogate(span[i]))
                {
                    return text[..(i + 1)];
                }
            }

            return text[..maxLength];
        }



    }
}
